﻿//
// <author>David Nohejl</author>
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WPFTrees.DataStructures;
using System.Windows.Media;
using System.Windows;
using System.IO;
using System.Globalization;

namespace WPFTrees.Parser
{
    /// <summary>
    /// Trida provadejici parsovani stromu ve formatu Newick.
    /// </summary>
    class NewickParser
    {

        /// <summary>
        /// Parse tree from string.
        /// </summary>
        /// <param name="input">string containing tree in Newick format.</param>
        /// <returns>Parsed tree.</returns>
        /// <exception cref="ParserException">
        /// Input string is not valid tree in Newick format.</exception>
        /// <remarks>
        /// http://evolution.genetics.washington.edu/phylip/newick_doc.html
        /// </remarks>        
        public static NTree<NewickNode> Parse(string input)
        {
            List<Token> tokens = Lexer.Tokenize(input);
            return Parse(tokens);
        }

        /// <summary>
        /// Parse tree from stream.
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static NTree<NewickNode> Parse(Stream stream)
        {
            using (StreamReader reader = new StreamReader(stream))
            {
                return NewickParser.Parse(reader.ReadToEnd());
            }
        }

        static double ParseBranchLenght(Token branchLenghtToken)
        {
            double d;
            string brachString = branchLenghtToken.Value/*.Substring(1)*/;
            if (double.TryParse(brachString, NumberStyles.Float, 
                CultureInfo.InvariantCulture, out d))
            {
                return d;
            }
            else
                throw new ParserException(
                    string.Format("Malformed branch lenght: {0}",
                    branchLenghtToken.Value));
        }

        static NTree<NewickNode> Parse(List<Token> tokens)
        {
            TreeInfo<NewickNode> info = new TreeInfo<NewickNode>();
            int maxChildren = 0;
            
            NTree<NewickNode> tree = new NTree<NewickNode>();
            NewickNode root = new NewickNode("<root>",false);
            tree.AddRoot(new NTreeNode<NewickNode>(root,100));
            int i = 0;
            Stack<Token> stack = new Stack<Token>();
            Stack<NTreeNode<NewickNode>> nodeStack = new Stack<NTreeNode<NewickNode>>();
            nodeStack.Push(tree.GetRoot());
            // projdu tokeny a zpracuju je
            while (i < tokens.Count)
            {
                if (tokens[i].Type == TokenType.OpeningBracket)
                {
                    //
                    // Oteviraci zavorka znaci podstrom, tudiz vytvorim novy 
                    // vnitrni uzel a dam ho na nodeStack.                    
                    //
                    #region Opening Bracket
                    nodeStack.Push(new NTreeNode<NewickNode>(
                                    new NewickNode("",false),100)
                                   );
                    #endregion
                }
                else if (tokens[i].Type == TokenType.ClosingBracket)
                {
                    //
                    // Zaviraci zavorka znaci konec podstromu, tudiz pro obsah 
                    // zasobniku vytvorim uzly a pridam je jako listy prave 
                    // zpracovavaneho podstromu. Ten odeberu ze zasobniku.
                    //
                    #region Closing Bracket

                    
                    NTreeNode<NewickNode> top = nodeStack.Pop();
                    if (i + 1 < tokens.Count)
                    {
                        if (tokens[i + 1].Type == TokenType.Label)
                        {
                            //Zpracuj branch lenght        
                            top.Value().Label = tokens[i + 1].Value;
                            i++;
                        }
                    }
                    if (i + 1 < tokens.Count)
                    {
                        if (tokens[i + 1].Type == TokenType.BranchLenght)
                        {
                            //Zpracuj branch lenght        
                            top.Value().BranchLenght = ParseBranchLenght(tokens[i + 1]);
                            i++;
                        }
                    }
                    nodeStack.Peek().AddNode(top);
                    #endregion
                }
                else if (tokens[i].Type == TokenType.Label)
                {
                    //
                    // Narazil sem na label, tudiz vytvorim list a pripadne 
                    // prectu a pridam branch lenght.
                    //
                    #region Label
                    NewickNode node = new NewickNode(tokens[i].Value,true);
                    if(i+1 < tokens.Count)
                    {
                        if (tokens[i+1].Type == TokenType.BranchLenght)
                            {                                 
                                //Zpracuj branch lenght        
                                node.BranchLenght = ParseBranchLenght(tokens[i+1]);
                                i++;
                            }
                    }
                    NTreeNode<NewickNode> leaf = new NTreeNode<NewickNode>(node,100);
                    NTreeNode<NewickNode> parent = nodeStack.Peek();
                    parent.AddNode(leaf);
                    maxChildren = Math.Max(maxChildren, parent.CountImmediateChildren);                        
                    info.Leaves.Add(leaf);
                    #endregion
                }                      
                i++;
            }
            if (nodeStack.Count > 1) 
            {
                throw new ParserException("Missing closing ')'.");
            }

            tree.AddRoot(tree.GetRoot().Children[0]);
            tree.Info = info;
            info.IsBinary = (maxChildren <=2);
            return tree;           
        }
    }   
}
